<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
    <meta name="description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta property="og:title" content="Bloom | 3D Game Shaders For Beginners" />
    <meta property="og:description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta property="og:image" content="https://i.imgur.com/brdytrF.png" />
    <meta name="twitter:title" content="Bloom | 3D Game Shaders For Beginners" />
    <meta name="twitter:description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta name="twitter:image" content="https://i.imgur.com/brdytrF.png" />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="author" content="David Lettier" />
    <title>Bloom | 3D Game Shaders For Beginners</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
    </style>
    <style>
      code.sourceCode > span { display: inline-block; line-height: 1.25; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      code.sourceCode > span:empty { height: 1.2em; }
      .sourceCode { overflow: visible; }
      code.sourceCode { white-space: pre; position: relative; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      code.sourceCode { white-space: pre-wrap; }
      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          background-color: #232629;
          color: #7a7c7d;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }
      div.sourceCode
        { color: #cfcfc2; background-color: #232629; }
      @media screen {
      code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span. { color: #cfcfc2; } /* Normal */
      code span.al { color: #95da4c; } /* Alert */
      code span.an { color: #3f8058; } /* Annotation */
      code span.at { color: #2980b9; } /* Attribute */
      code span.bn { color: #f67400; } /* BaseN */
      code span.bu { color: #7f8c8d; } /* BuiltIn */
      code span.cf { color: #fdbc4b; } /* ControlFlow */
      code span.ch { color: #3daee9; } /* Char */
      code span.cn { color: #27aeae; } /* Constant */
      code span.co { color: #7a7c7d; } /* Comment */
      code span.cv { color: #7f8c8d; } /* CommentVar */
      code span.do { color: #a43340; } /* Documentation */
      code span.dt { color: #2980b9; } /* DataType */
      code span.dv { color: #f67400; } /* DecVal */
      code span.er { color: #da4453; } /* Error */
      code span.ex { color: #0099ff; } /* Extension */
      code span.fl { color: #f67400; } /* Float */
      code span.fu { color: #8e44ad; } /* Function */
      code span.im { color: #27ae60; } /* Import */
      code span.in { color: #c45b00; } /* Information */
      code span.kw { color: #cfcfc2; } /* Keyword */
      code span.op { color: #cfcfc2; } /* Operator */
      code span.ot { color: #27ae60; } /* Other */
      code span.pp { color: #27ae60; } /* Preprocessor */
      code span.re { color: #2980b9; } /* RegionMarker */
      code span.sc { color: #3daee9; } /* SpecialChar */
      code span.ss { color: #da4453; } /* SpecialString */
      code span.st { color: #f44f4f; } /* String */
      code span.va { color: #27aeae; } /* Variable */
      code span.vs { color: #da4453; } /* VerbatimString */
      code span.wa { color: #da4453; } /* Warning */
    </style>
    <!--[if lt IE 9]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
<p><a href="blur.html"><span class="emoji" data-emoji="arrow_backward">◀️</span></a> <a href="index.html"><span class="emoji" data-emoji="arrow_double_up">⏫</span></a> <a href="#"><span class="emoji" data-emoji="arrow_up_small">🔼</span></a> <a href="#copyright"><span class="emoji" data-emoji="arrow_down_small">🔽</span></a> <a href="ssao.html"><span class="emoji" data-emoji="arrow_forward">▶️</span></a></p>
<h1 id="3d-game-shaders-for-beginners">3D Game Shaders For Beginners</h1>
<h2 id="bloom">Bloom</h2>
<p><img src="https://i.imgur.com/UxKRz2r.gif" alt="Bloom" /></p>
<p>Adding bloom to a scene can really sell the illusion of the lighting model. Light emitting objects are more believable and specular highlights get an extra dose of shimmer.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a>  <span class="co">//...</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a>  <span class="dt">int</span>   size       = <span class="dv">5</span>;</span>
<span id="cb1-4"><a href="#cb1-4"></a>  <span class="dt">float</span> separation = <span class="dv">3</span>;</span>
<span id="cb1-5"><a href="#cb1-5"></a>  <span class="dt">float</span> threshold  = <span class="fl">0.4</span>;</span>
<span id="cb1-6"><a href="#cb1-6"></a>  <span class="dt">float</span> amount     = <span class="dv">1</span>;</span>
<span id="cb1-7"><a href="#cb1-7"></a></span>
<span id="cb1-8"><a href="#cb1-8"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>These parameters control the look and feel. <code>size</code> determines how blurred the effect is. <code>separation</code> spreads out the blur. <code>threshold</code> controls which fragments are illuminated. And the last parameter, <code>amount</code>, controls how much bloom is outputted.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a>  <span class="co">// ...</span></span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a>  vec2 texSize = textureSize(colorTexture, <span class="dv">0</span>).xy;</span>
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a>  <span class="dt">float</span> value = <span class="dv">0</span>;</span>
<span id="cb2-6"><a href="#cb2-6"></a></span>
<span id="cb2-7"><a href="#cb2-7"></a>  vec4 result = vec4(<span class="dv">0</span>);</span>
<span id="cb2-8"><a href="#cb2-8"></a>  vec4 color  = vec4(<span class="dv">0</span>);</span>
<span id="cb2-9"><a href="#cb2-9"></a></span>
<span id="cb2-10"><a href="#cb2-10"></a>  <span class="cf">for</span> (<span class="dt">int</span> i = -size; i &lt;= size; ++i) {</span>
<span id="cb2-11"><a href="#cb2-11"></a>    <span class="cf">for</span> (<span class="dt">int</span> j = -size; j &lt;= size; ++j) {</span>
<span id="cb2-12"><a href="#cb2-12"></a>      <span class="co">// ...</span></span>
<span id="cb2-13"><a href="#cb2-13"></a>    }</span>
<span id="cb2-14"><a href="#cb2-14"></a>  }</span>
<span id="cb2-15"><a href="#cb2-15"></a></span>
<span id="cb2-16"><a href="#cb2-16"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>The technique starts by looping through a kernel/matrix/window centered over the current fragment. This is similar to the window used for <a href="outlining.html">outlining</a>. The size of the window is <code>size * 2 + 1</code> by <code>size * 2 + 1</code>. So for example, with a <code>size</code> setting of two, the window uses <code>(2 * 2 + 1)^2 = 25</code> samples per fragment.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a>      <span class="co">// ...</span></span>
<span id="cb3-2"><a href="#cb3-2"></a></span>
<span id="cb3-3"><a href="#cb3-3"></a>      color =</span>
<span id="cb3-4"><a href="#cb3-4"></a>        texture</span>
<span id="cb3-5"><a href="#cb3-5"></a>          ( colorTexture</span>
<span id="cb3-6"><a href="#cb3-6"></a>          ,   ( gl_FragCoord.xy</span>
<span id="cb3-7"><a href="#cb3-7"></a>              + (vec2(i, j) * separation)</span>
<span id="cb3-8"><a href="#cb3-8"></a>              )</span>
<span id="cb3-9"><a href="#cb3-9"></a>            / texSize</span>
<span id="cb3-10"><a href="#cb3-10"></a>          );</span>
<span id="cb3-11"><a href="#cb3-11"></a></span>
<span id="cb3-12"><a href="#cb3-12"></a>      value = max(color.r, max(color.g, color.b));</span>
<span id="cb3-13"><a href="#cb3-13"></a>      <span class="cf">if</span> (value &lt; threshold) { color = vec4(<span class="dv">0</span>); }</span>
<span id="cb3-14"><a href="#cb3-14"></a></span>
<span id="cb3-15"><a href="#cb3-15"></a>      result.rgb += color.rgb;</span>
<span id="cb3-16"><a href="#cb3-16"></a></span>
<span id="cb3-17"><a href="#cb3-17"></a>      <span class="co">// ...</span></span></code></pre></div>
<p>For each iteration, it retrieves the color from the input texture and turns the red, green, and blue values into a greyscale value. If this greyscale value is less than the threshold, it discards this color by making it solid black. After evaluating the sample's greyscale value, it adds its RGB values to <code>result</code>.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a>  <span class="co">// ...</span></span>
<span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb4-3"><a href="#cb4-3"></a>  result.rgb /= pow(size * <span class="dv">2</span> + <span class="dv">1</span>, <span class="dv">2</span>);</span>
<span id="cb4-4"><a href="#cb4-4"></a>  result.a = <span class="dv">1</span>;</span>
<span id="cb4-5"><a href="#cb4-5"></a></span>
<span id="cb4-6"><a href="#cb4-6"></a>  fragColor = mix(vec4(<span class="dv">0</span>), result, amount);</span>
<span id="cb4-7"><a href="#cb4-7"></a></span>
<span id="cb4-8"><a href="#cb4-8"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>After it's done summing up the samples, it divides the sum of the color samples by the number of samples taken. The result is the average color of itself and its neighbors. By doing this for every fragment, you end up with a blurred image. This form of blurring is known as a <a href="blur.html#box-blur">box blur</a>.</p>
<p><img src="https://i.imgur.com/m4yedrM.gif" alt="Bloom progresssion." /></p>
<p>Here you see the progression of the bloom algorithm.</p>
<h3 id="source">Source</h3>
<ul>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx" target="_blank" rel="noopener noreferrer">main.cxx</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert" target="_blank" rel="noopener noreferrer">basic.vert</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/outline.frag" target="_blank" rel="noopener noreferrer">bloom.frag</a></li>
</ul>
<h2 id="copyright">Copyright</h2>
<p>(C) 2019 David Lettier <br> <a href="https://www.lettier.com">lettier.com</a></p>
<p><a href="blur.html"><span class="emoji" data-emoji="arrow_backward">◀️</span></a> <a href="index.html"><span class="emoji" data-emoji="arrow_double_up">⏫</span></a> <a href="#"><span class="emoji" data-emoji="arrow_up_small">🔼</span></a> <a href="#copyright"><span class="emoji" data-emoji="arrow_down_small">🔽</span></a> <a href="ssao.html"><span class="emoji" data-emoji="arrow_forward">▶️</span></a></p>
  </body>
</html>
